package com.lan.parse;

import com.lan.ScriptContext;
import com.lan.antlr.ScriptBaseListener;
import com.lan.antlr.ScriptParser;
import com.lan.model.Type;
import com.lan.model.symbol.*;
import org.antlr.v4.runtime.ParserRuleContext;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Stack;

public class ParseJslListener extends ScriptBaseListener {

    private ScriptContext context;

    private Stack<Scope> scopeStack = new Stack<>();


    public ParseJslListener(ScriptContext context) {
        this.context = context;
    }

    private void print(ParserRuleContext ctx, String name) {
        System.out.println(ctx.getRuleIndex() + " :   " + ctx.toString() + "   " + (StringUtils.isNotBlank(name) ? name : "") + " " + (StringUtils.isNotBlank(ctx.getText()) ? ctx.getText() : ""));
    }

    private Scope pushScope(Scope scope, ParserRuleContext ctx) {
        context.nodeOfScope(ctx, scope);
        scope.setCtx(ctx);
        scopeStack.push(scope);
        return scope;
    }

    /**
     * 从栈中弹出scope
     */
    private void popScope() {
        scopeStack.pop();
    }

    /**
     * 在遍历树的过程中，当前的Scope
     *
     * @return
     */
    private Scope scope() {
        if (scopeStack.size() > 0) {
            return scopeStack.peek();
        } else {
            return null;
        }
    }

    private String getName() {
        Scope scope = scope();
        if (scope != null) {
            return scope.getId();
        }
        return "";
    }

    @Override
    public void enterProg(ScriptParser.ProgContext ctx) {
        Ns scope = new Ns("ns", "ns", scope(), ctx);
        context.setNs(scope);
        pushScope(scope, ctx);
        print(ctx, "enterProg");
    }

    @Override
    public void exitProg(ScriptParser.ProgContext ctx) {
        popScope();
        print(ctx, "exitProg");
    }

    @Override
    public void enterStatement(ScriptParser.StatementContext ctx) {
        //为for建立额外的Scope
        if (ctx.FOR() != null) {
            String name = getName();
            Block scope = new Block(name, "for", scope(), ctx);
            scope().add(scope);
            pushScope(scope, ctx);
            print(ctx, "enterStatement");
        }
    }

    @Override
    public void exitStatement(ScriptParser.StatementContext ctx) {
        //释放for语句的外层scope
        if (ctx.FOR() != null) {
            popScope();
            print(ctx, "exitStatement");
        }
    }

    @Override
    public void enterVariableInitializer(ScriptParser.VariableInitializerContext ctx) {
        if (ctx.function() != null && ctx.parent instanceof ScriptParser.VariableDeclaratorContext) {
            ScriptParser.VariableDeclaratorContext variableDeclaratorContext = (ScriptParser.VariableDeclaratorContext) ctx.parent;
            String name = variableDeclaratorContext.variableDeclaratorId().name().getText();
            //注意：目前function的信息并不完整，参数要等到TypeResolver.java中去确定。
            Function function = new Function(getName() + "_" + name, name, scope(), ctx);
            scope().add(function);

            // 创建一个新的scope
            pushScope(function, ctx);
            print(ctx, "enterFunctionDeclaration");
        }
    }

    @Override
    public void exitVariableInitializer(ScriptParser.VariableInitializerContext ctx) {
        if (ctx.function() != null && ctx.parent instanceof ScriptParser.VariableDeclaratorContext) {
            popScope();
            print(ctx, "exitVariableInitializer");
        }
    }


    @Override
    public void enterJson(ScriptParser.JsonContext ctx) {
        ScriptParser.VariableInitializerContext variableInitializerContext = (ScriptParser.VariableInitializerContext) ctx.parent;
        //第一层 scope
        String name = null;
        if (variableInitializerContext.parent instanceof ScriptParser.VariableDeclaratorContext) {
            ScriptParser.VariableDeclaratorContext variableDeclaratorContext = (ScriptParser.VariableDeclaratorContext) variableInitializerContext.parent;
            ScriptParser.VariableDeclaratorIdContext variableDeclaratorIdContext = variableDeclaratorContext.variableDeclaratorId();
            name = variableDeclaratorIdContext.getText();
        } else if (variableInitializerContext.parent instanceof ScriptParser.StatementContext) {
            name = "RETURN";
        } else {
            return;
        }
        if (ctx.obj() != null) {
            Json json = new Json(getName() + "_" + name, name, scope(), ctx);
            scope().add(json);
            // 创建一个新的scope
            pushScope(json, ctx);
        } else if (ctx.arr() != null) {
            Jarray json = new Jarray(getName() + "_" + name, name, scope(), ctx);
            scope().add(json);
            // 创建一个新的scope
            pushScope(json, ctx);
        }
    }


    @Override
    public void enterObj(ScriptParser.ObjContext ctx) {
        List<ScriptParser.PairContext> pairContextList = ctx.pair();
        if (pairContextList != null && pairContextList.size() > 0) {
            for (int i = 0; i < pairContextList.size(); i++) {
                ScriptParser.PairContext item = pairContextList.get(i);
                String name = item.str().name().getText();

                if (item.value().function() != null) {
                    Function function = new Function(getName() + "_" + name, name, scope(), item.value().function());
                    scope().add(function);
                    // 创建一个新的scope
                    pushScope(function, ctx);
                } else if (item.value().json() != null) {
                    if (item.value().json().obj() != null) {
                        Json json = new Json(getName() + "_" + name, name, scope(), ctx);
                        scope().add(json);
                        // 创建一个新的scope
                        pushScope(json, ctx);
                    } else if (item.value().json().arr() != null) {
                        Jarray json = new Jarray(getName() + "_" + name, name, scope(), ctx);
                        scope().add(json);
                        // 创建一个新的scope
                        pushScope(json, ctx);
                    }
                } else if (item.value().literal() != null) {
                    jsonPair(item, name);
                }
            }
        }
    }

    @Override
    public void exitObj(ScriptParser.ObjContext ctx) {
        List<ScriptParser.PairContext> pairContextList = ctx.pair();
        if (pairContextList != null && pairContextList.size() > 0) {
            for (int i = 0; i < pairContextList.size(); i++) {
                ScriptParser.PairContext item = pairContextList.get(i);
                if (item.value().function() != null) {
                    popScope();
                } else if (item.value().json() != null) {
                    popScope();
                }
            }
        }
    }


    private void jsonPair(ScriptParser.PairContext pairContext, String name) {
        Scope scope = scope();
        String id = getName();
        Variable variable = new Variable(id + "_" + name, name, scope, pairContext.str());
        ScriptParser.LiteralContext literalContext = pairContext.value().literal();
        if (literalContext.DECIMAL_LITERAL() != null) {
            variable.setType(Type.Integer);
            variable.setValue(Integer.valueOf(literalContext.DECIMAL_LITERAL().getText()));
        } else if (literalContext.FLOAT_LITERAL() != null) {
            variable.setType(Type.Float);
            variable.setValue(Float.valueOf(literalContext.FLOAT_LITERAL().getText()));
        } else if (literalContext.BOOL_LITERAL() != null) {
            variable.setType(Type.Boolean);
            variable.setValue(Boolean.valueOf(literalContext.BOOL_LITERAL().getText()));
        } else if (literalContext.str() != null) {
            variable.setType(Type.String);
            if (literalContext.str().name() != null) {
                variable.setValue(literalContext.str().name());
            }
        }
        scope.add(variable);
    }


    @Override
    public void enterArr(ScriptParser.ArrContext ctx) {
        String name = getName();
        for (int i = 0; i < ctx.value().size(); i++) {
            ScriptParser.ValueContext valueContext = ctx.value(i);
            if (valueContext.function() != null) {
                Function function = new Function(name + "_array_" + i, i + "", scope(), ctx);
                // 创建一个新的scope
                pushScope(function, valueContext.function());
                popScope();
            } else if (valueContext.json() != null) {
                if (valueContext.json().obj() != null) {
                    Json json = new Json(getName() + "_array_" + i, i + "", scope(), ctx);
                    scope().add(json);
                    // 创建一个新的scope
                    pushScope(json, ctx);
                } else if (valueContext.json().arr() != null) {
                    Jarray json = new Jarray(getName() + "_array_" + i, i + "", scope(), ctx);
                    scope().add(json);
                    // 创建一个新的scope
                    pushScope(json, ctx);
                }
            }
        }
    }

    @Override
    public void exitJson(ScriptParser.JsonContext ctx) {
        ScriptParser.VariableInitializerContext variableInitializerContext = (ScriptParser.VariableInitializerContext) ctx.parent;
        if (variableInitializerContext.parent instanceof ScriptParser.VariableDeclaratorContext) {
            popScope();
        } else if (variableInitializerContext.parent instanceof ScriptParser.StatementContext) {
            popScope();
        }
    }


    @Override
    public void exitArr(ScriptParser.ArrContext ctx) {
        for (int i = 0; i < ctx.value().size(); i++) {
            ScriptParser.ValueContext valueContext = ctx.value(i);
            if (valueContext.function() != null) {
                popScope();
            } else if (valueContext.json() != null) {
                if (valueContext.json().obj() != null) {
                    popScope();
                } else if (valueContext.json().arr() != null) {
                    popScope();
                }
            }
        }
    }


}
